package com.uatair.common.mqtt.uploader;

import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;
import com.uatair.common.core.redis.RedisCache;
import com.uatair.common.kafka.domain.protobuf.FlightProto;
import com.uatair.common.mqtt.domain.dto.ApiUploadResponse;
import com.uatair.common.mqtt.domain.task.RecodeTask;
import com.uatair.common.mqtt.domain.task.SortieTask;
import com.uatair.common.mqtt.domain.task.TaskDTO;
import com.uatair.common.mqtt.domain.task.UavTask;
import com.uatair.common.mqtt.util.AESUtil;
import com.uatair.common.mqtt.util.HttpClientUtil;
import com.uatair.common.mqtt.util.ProtobufUtil;
import com.uatair.common.mqtt.util.UAesUtil;
import com.uatair.common.service.DroneDataService;
import com.uatair.common.tcp.builder.MessageBuilder;
import com.uatair.common.tcp.client.TcpClientService;
import com.uatair.common.tcp.util.PositionData;
import com.uatair.common.tcp.util.ProtocolConstants;
import com.uatair.common.utils.StringUtils;
import com.uatair.common.utils.ip.AddressUtils;
import com.uatair.common.utils.uuid.UUID;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Component
public class HeNanTaskUploader {

    private static final Logger log = LoggerFactory.getLogger(HeNanTaskUploader.class);

    private static final AtomicInteger sequence = new AtomicInteger(0);

    public static final String HENAN_DOWNLOAD = "uav:henan:";
    public static final long FLIGHT_TTL_DAYS = 30; // 30天TTL

    private final TcpClientService tcpClientService;

    private final DroneDataService droneDataService;

    private final RedisCache redisCache;

    public HeNanTaskUploader(TcpClientService tcpClientService, DroneDataService droneDataService, RedisCache redisCache) {
        this.tcpClientService = tcpClientService;
        this.droneDataService = droneDataService;
        this.redisCache = redisCache;
    }

    public List<RecodeTask> putHeNanTask(TaskDTO taskDTO) {
        List<UavTask> uavTaskList = taskDTO.getUavTaskList();
        StringBuilder jsonBody = new StringBuilder("[");
        for (int i = 0; i < uavTaskList.size(); i++) {
            UavTask uavTask = uavTaskList.get(i);
            jsonBody.append(String.format(
                    "{\"factorySn\":\"%s\",\"bodyCode\":\"%s\",\"hardwareId\":\"%s\"," +
                            "\"uavMode\":\"%s\",\"droneType\":\"%s\",\"uavType\":\"%s\",\"bdIdentification\":\"%s\"}",
                    uavTask.getCodeOfSuit(), uavTask.getCodeOfBodyFrame(), uavTask.getCodeOfFlightControl(),
                    "Q100", "Q100", uavTask.getUavType(), "Y"
            ));

            if (i < uavTaskList.size() - 1) {
                jsonBody.append(",");
            }
        }
        jsonBody.append("]");
        Map<String, String> map = new HashMap<String, String>();
        map.put("token","4a8a7693796b8f7c47aed240354ce029");
        String s = HttpClientUtil.doPostJson("http://tpa.chiaic.com/v1/drone/sync", jsonBody.toString(), map);
        log.info("无人机信息传输返回：{}",s);
        List<RecodeTask> recodeTaskList = new ArrayList<>();
        List<SortieTask> agSorties = taskDTO.getSortieTaskList();
        for (SortieTask sortieTask : agSorties) {
            String factorySn = "";
            for (UavTask uavTask : uavTaskList){
                if (uavTask.getSn().equals(sortieTask.getSn()))
                    factorySn = uavTask.getCodeOfSuit();
            }
            byte[] authMsg = MessageBuilder.buildAuthMessage(
//                    sortieTask.getSn(),
                    factorySn,
                    System.currentTimeMillis(),
                    (short) 31471,
                    "V5.6.5",
                    "H1.13.9",
                    (short)sequence.getAndIncrement()
            );
            tcpClientService.connect();
            tcpClientService.sendMessage(authMsg);
            setRecodeTask(recodeTaskList, taskDTO, sortieTask, authMsg.toString(), "无人机鉴权上报");
            //解析txt轨迹
            if (sortieTask.getPath() != null) {
                List<String> strings = UAesUtil.downloadAndParseFile(sortieTask.getPath());
                if (!strings.isEmpty()) {
                    List<JSONObject> traceJson = AESUtil.getJsonObjects(strings);
                    long startMillis = sortieTask.getStartTime().toInstant().toEpochMilli();
                    long endMillis = sortieTask.getEndTime().toInstant().toEpochMilli();
                    long totalMillis = endMillis - startMillis;
                    double intervalMillis = (double) totalMillis / ((traceJson.size()==1?2:traceJson.size()) - 1);
                    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    for (int i = 0; i < traceJson.size(); i++) {
                        JSONObject osd = traceJson.get(i);
                        // 计算当前时间点
                        long currentMillis;
                        if (i == traceJson.size() - 1) {
                            // 最后一个点直接使用结束时间，避免浮点误差
                            currentMillis = endMillis;
                        } else {
                            // 使用四舍五入来减少累积误差
                            currentMillis = startMillis + Math.round(i * intervalMillis);
                        }
                        double vx = osd.getDouble("vx");
                        double vy = osd.getDouble("vy");
                        double vz = osd.getDouble("vz");
                        double v = UAesUtil.calculateSpeedFromComponents(vx,vy,vz);
                        DecimalFormat df = new DecimalFormat("#.##");
                        DecimalFormat df1 = new DecimalFormat("#");

                        PositionData positionData = new PositionData();
                        positionData.timestamp = currentMillis;
                        log.info("第 {} 组轨迹数据，时间戳：{}，转为时间格式：{}", i , currentMillis, dateFormat.format(new Date(currentMillis)));
                        positionData.positionStatus = ProtocolConstants.POS_STATUS_SINGLE;
                        positionData.longitude = Double.parseDouble(osd.get("lon").toString());
                        positionData.latitude = Double.parseDouble(osd.get("lat").toString());
                        positionData.altitude = (short) Double.parseDouble(osd.get("alt").toString());
                        positionData.speed = (short) Double.parseDouble(df.format(v * 3.6));
                        positionData.direction = (short) Double.parseDouble(osd.get("heading").toString());
                        positionData.rowSpacing = sortieTask.getLineSpacing() == null ? 0 : sortieTask.getLineSpacing().byteValue();
                        Integer sprayType = 0;
                        if (osd.has("workArea")) {
                            double workArea = Double.parseDouble(osd.get("workArea").toString());
                            sprayType = workArea > 1 ? workArea < sortieTask.getSize() ? 1 : 0 : 0;
                            positionData.area = sortieTask.getSize() == null ? 0 : Byte.parseByte(df1.format(sortieTask.getSize()*0.0015));
                        } else {
                            double alt = Double.parseDouble(osd.get("relativeAlt").toString());
                            if (alt > 3)
                                sprayType = 1;
                        }
                        positionData.workStatus = sprayType.byteValue();
                        positionData.workNum = (short) (traceJson.size() - i - 1);
                        log.info("设备编号：{}，架次数据：{}", sortieTask.getSn(), positionData);
                        byte[] positionMsg = MessageBuilder.buildPositionMessage(
//                                sortieTask.getSn(),
                                factorySn,
                                (short)sequence.getAndIncrement(),
                                positionData
                        );
                        tcpClientService.sendMessage(positionMsg);
                    }
                    setRecodeTask(recodeTaskList, taskDTO, sortieTask, "上传作业轨迹共计：" + traceJson.size() + "条", "无人机轨迹上报");
                }
            }
            tcpClientService.close();
        }
        return recodeTaskList;
    }

    public List<RecodeTask> putHeNanKafkaTask(TaskDTO taskDTO){
        List<RecodeTask> recodeTaskList = new ArrayList<>();
        List<SortieTask> agSorties = taskDTO.getSortieTaskList();
        for (SortieTask sortieTask : agSorties) {
            //解析txt轨迹
            if (sortieTask.getPath() != null) {
                List<String> strings = UAesUtil.downloadAndParseFile(sortieTask.getPath());
                if (!strings.isEmpty()) {
                    List<JSONObject> traceJson = AESUtil.getJsonObjects(strings);
                    //存入redis 缓解下载压力
                    Map<String, String> flightRecord = redisCache.getCacheMap(HeNanTaskUploader.HENAN_DOWNLOAD + sortieTask.getSortieNo());
                    if (flightRecord == null || flightRecord.isEmpty()) {
                        List<FlightProto.FlightData> flightDataList = new ArrayList<>();
                        long startMillis = sortieTask.getStartTime().toInstant().toEpochMilli();
                        long endMillis = sortieTask.getEndTime().toInstant().toEpochMilli();
                        long totalMillis = endMillis - startMillis;
                        double intervalMillis = (double) totalMillis / ((traceJson.size()==1?2:traceJson.size()) - 1);
                        int optMode = Integer.parseInt(sortieTask.getOptMode());
                        for (int i = 0; i < traceJson.size(); i++) {
                            JSONObject osd = traceJson.get(i);
                            int sprayType = 0;
                            if (osd.has("workArea")) {
                                double workArea = Double.parseDouble(osd.get("workArea").toString());
                                sprayType = workArea > 1 ? workArea < sortieTask.getSize() ? 1 : 0 : 0;
                            } else {
                                double alt = Double.parseDouble(osd.get("relativeAlt").toString());
                                if (alt > 3)
                                    sprayType = 1;
                            }
                            long currentMillis;
                            if (i == traceJson.size() - 1) {
                                // 最后一个点直接使用结束时间，避免浮点误差
                                currentMillis = endMillis;
                            } else {
                                // 使用四舍五入来减少累积误差
                                currentMillis = startMillis + Math.round(i * intervalMillis);
                            }
                            FlightProto.SprayInfo sprayInfo;
                            FlightProto.SowInfo sowInfo;
                            if (optMode == 0) {
                                sprayInfo = FlightProto.SprayInfo.newBuilder()
                                        .setSprayType(sprayType)
                                        .setFlowSpeed(sortieTask.getPumpSpeed().intValue())
                                        .build();
                                sowInfo = FlightProto.SowInfo.newBuilder()
                                        .setSowType(0)
                                        .build();

                            } else {
                                sprayInfo = FlightProto.SprayInfo.newBuilder()
                                        .setSprayType(0)
                                        .build();
                                sowInfo = FlightProto.SowInfo.newBuilder()
                                        .setSowType(sprayType)
                                        .setSowSpeed(sortieTask.getPumpSpeed().intValue())
                                        .build();

                            }
                            FlightProto.FlightData flightData = FlightProto.FlightData.newBuilder()
                                    .setCollectTime(currentMillis)
                                    .setPosition(FlightProto.Position.newBuilder()
                                            .setLatitude(Double.parseDouble(osd.get("lat").toString()))
                                            .setLongitude(Double.parseDouble(osd.get("lon").toString()))
                                            .build())
                                    .setVelocity(FlightProto.Velocity.newBuilder()
                                            .setX(Float.parseFloat(osd.get("vx").toString()))
                                            .setY(Float.parseFloat(osd.get("vy").toString()))
                                            .setZ(Float.parseFloat(osd.get("vz").toString()))
                                            .build())
                                    .setHeight(Float.parseFloat(osd.get("alt").toString()))
                                    .setWorkMode("5".equals(sortieTask.getRouteMode()) ? 1 : 4)
                                    .setSprayWidth(Float.parseFloat(sortieTask.getLineSpacing() == null ? "0" : sortieTask.getLineSpacing().toString()))
                                    .setSprayInfo(sprayInfo)
                                    .setSowInfo(sowInfo)
                                    .build();
                            flightDataList.add(flightData);
                        }
                        if (!flightDataList.isEmpty()) {
                            FlightProto.FlightRecordDetail flightRecordDetail = FlightProto.FlightRecordDetail.newBuilder()
                                    .setCount(flightDataList.size())
                                    .setUsageType(optMode) // 0:喷洒 1:播撒
                                    .addAllData(flightDataList)
                                    .build();
                            try {
                                //存入redis
                                Map<String, String> redisFlightRecord = new HashMap<>();  // 注意Map值类型为byte[]
                                String jsonData = JsonFormat.printer().print(flightRecordDetail);
                                redisFlightRecord.put("flightData", jsonData);
                                String key = HENAN_DOWNLOAD + sortieTask.getSortieNo();
                                redisCache.setCacheMap(key, redisFlightRecord);
                                redisCache.expire(key, FLIGHT_TTL_DAYS, TimeUnit.DAYS);
                            } catch (InvalidProtocolBufferException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                    Map<String, String> map = new HashMap<>();
                    if (StringUtils.isNotEmpty(sortieTask.getPositionInfo()))
                        map = AddressUtils.parseChineseAddress(sortieTask.getPositionInfo());
                    String bodyFrame = "";//机架号
                    String suit = "";//飞控序列号
                    for (UavTask uavTask : taskDTO.getUavTaskList()){
                        if (uavTask.getSn().equals(sortieTask.getSn())){
                            bodyFrame = uavTask.getCodeOfBodyFrame();
                            suit = uavTask.getCodeOfSuit();
                        }
                    }
                    FlightProto.FlightRecord record = FlightProto.FlightRecord.newBuilder()
                            // 1. 基础信息
                            .setUuid(UUID.randomUUID().toString())
                            .setCreatedAt(sortieTask.getEndTime().getTime())
                            .setUpdatedAt(sortieTask.getEndTime().getTime())
                            // 3. 航点数据下载地址
                            .setDetailUrl("http://39.108.218.253/xcr/system/user/profile/download?sortieNo=" + sortieTask.getSortieNo())
                            // 4. 时间信息
                            .setCollectTime(sortieTask.getStartTime().getTime())
                            .setWorkTime(sortieTask.getFlightTime().intValue()) // 1小时（毫秒）
                            .setBeginTimestamp(sortieTask.getStartTime().getTime())
                            .setEndTimestamp(sortieTask.getEndTime().getTime())
                            // 5. 位置信息
                            .setLocation(sortieTask.getPositionInfo() == null ? "广东省深圳市龙岗区园山街道" : sortieTask.getPositionInfo())
                            .setProvince(map.getOrDefault("province", "广东省"))
                            .setCity(map.getOrDefault("city", "深圳市"))
                            .setDistrict(map.getOrDefault("country", "龙岗区"))
                            .setTown(map.getOrDefault("town", "园山街道"))
                            .setPosition(FlightProto.Position.newBuilder()
                                    .setLatitude(Double.parseDouble(traceJson.get(0).get("lat").toString()))
                                    .setLongitude(Double.parseDouble(traceJson.get(0).get("lon").toString()))
                                    .build())
                            // 6. 作业参数
                            .setWorkArea(Float.parseFloat(String.format("%.2f", sortieTask.getSize() * 0.15))) // 150.5亩（单位0.01亩）
                            .setUsageType(0) // 0:喷洒
                            .setSprayUsage(sortieTask.getDosage().intValue()*1000)
                            .setSowUsage(0) // 0g
                            // 7. 飞机信息
                            .setDroneType("Q100")
                            .setHardwareId(sortieTask.getSn())
                            .setBodyCode(bodyFrame)
                            .setFactorySn(suit)
                            .setCompanyCode("LHFJ")
                            .setBdIdentification("Y")
                            .setUavMode("Q100")
                            .setUavType(0) // 0:多旋翼
                            .build();
                    boolean sent = droneDataService.sendFlightRecord(record);
                    try {
                        if (sent)
                            setKafkaTask(recodeTaskList, taskDTO, sortieTask, ProtobufUtil.toJson(record), "无人机作业上报",200);
                        else
                            setKafkaTask(recodeTaskList, taskDTO, sortieTask, ProtobufUtil.toJson(record), "无人机作业上报",500);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        return recodeTaskList;
    }

    private void setRecodeTask(List<RecodeTask> recodeTaskList, TaskDTO taskDTO, SortieTask sortieTask, String msg, String requestType) {
        ApiUploadResponse apiUploadResponse = new ApiUploadResponse("", "", "TCP/IP", 200);
//        String upperCase = new BigInteger(1, msg).toString(16).toUpperCase();
        JSONObject requestBody = new JSONObject();
        requestBody.put("sn", sortieTask.getSn());
        requestBody.put("massage", msg);
        RecodeTask recodeTask = ZhiBaoYunTaskUploader.getRecode(apiUploadResponse, requestBody, requestType);
        ZhiBaoYunTaskUploader.setTaskId(taskDTO, recodeTaskList, recodeTask);
    }

    private void setKafkaTask(List<RecodeTask> recodeTaskList, TaskDTO taskDTO, SortieTask sortieTask, String msg, String requestType,Integer code) {
        ApiUploadResponse apiUploadResponse = new ApiUploadResponse("", "", "Kafka", code);
        JSONObject requestBody = new JSONObject();
        requestBody.put("sn", sortieTask.getSn());
        requestBody.put("massage", msg);
        RecodeTask recodeTask = ZhiBaoYunTaskUploader.getRecode(apiUploadResponse, requestBody, requestType);
        ZhiBaoYunTaskUploader.setTaskId(taskDTO, recodeTaskList, recodeTask);
    }
}
